package pl.edu.agh.neuraleconomy.core.ta.advice;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import pl.edu.agh.neuraleconomy.model.exchange.Company;

public class AdvisorComposite implements IAdvisor {
	private List<IAdvisor> advisors = new LinkedList<IAdvisor>();
	private List<Double> weights = new LinkedList<Double>();

	public AdvisorComposite(){
		
	}
	
	public AdvisorComposite(WeightedAdvisor ... advisors){
		for(WeightedAdvisor adv : advisors){
			addAdvisor(adv.getAdvisor(), adv.getWeight());
		}
	}

	public AdvisorComposite(IAdvisor... advisors) {
		for (IAdvisor a : advisors) {
			addAdvisor(a);
		}
	}

	public Advice getAdvice(Company company, Date date) {
		List<Advice> advices = getAdvices(company, date);

		double sum = 0.0;
		double weightsSum = 0.0;

		for (int i = 0; i < advices.size(); i++) {
			Advice advice = advices.get(i);
			if (advice != null) {
				sum += weights.get(i) * getAdviceFactor(advice);
				weightsSum += weights.get(i);
			}
		}

		double avgerage = sum / weightsSum;

		return getAdvice(company, avgerage);
	}

	private Advice getAdvice(Company company, double average) {
		double threshold = 40.0;

		if (average > threshold) {
			return new Advice(company, AdviceType.BUY, (int) average);
		}

		if (average < -threshold) {
			return new Advice(company, AdviceType.SELL, (int) Math.abs(average));
		}

		return new Advice(company, AdviceType.STAY, 0);
	}

	private List<Advice> getAdvices(Company company, Date date) {
		List<Advice> advices = new LinkedList<Advice>();
		for (IAdvisor advisor : advisors) {
			advices.add(advisor.getAdvice(company, date));
		}
		return advices;
	}

	public void addAdvisor(IAdvisor advisor) {
		addAdvisor(advisor, 1.0);
	}

	public void addAdvisor(IAdvisor advisor, Double weight) {
		advisors.add(advisor);
		weights.add(weight);
	}

	private double getAdviceFactor(Advice advice) {
		switch (advice.getType()) {
		case BUY:
			return advice.getCertainty();
		case SELL:
			return -advice.getCertainty();
		default:
			return 0.0;
		}

	}
	
	@AllArgsConstructor
	@Data
	public static class WeightedAdvisor {
		private IAdvisor advisor;
		private Double weight; 
	}

}
